# Finserver

*Для реализации пет-проекта я хочу взять за основу свою Excel таблицу по финансам и реализовать удобный функционал по вводу данных о расхода и доходах, возможность их редактировать и просмотр автоматически созданной статистики*

При выполнении проекта, необходимо обратить особое внимание, на application.properties, возможно заменить его на более удобный формат .yaml, обратить внимание на зависимости maven в pom.xml, чтобы не было лишних зависимостей и *жирности* итогового проекта. Обеспечить запуск сервисов и базы данных в docker контейнерах.

## Состав проекта
### **Пользователи**

У пользователя будет возможность зарегистрироваться и поработать со своей учетной записью в меню информации о пользователе, а также по необходимости скорректировать данные о себе.

Во-первых, нужно реализовать регистрацию пользователя и обеспечить надежное хранение  пароля в БД, а также написать валидацию и авторизацию пользователя. Данный вопрос решается с помощью *Spring Security*.

После того как пользователь зайдет в систему под своими учетными данными, он сможет посмотреть информацию о себе, по необходимости изменить имя на любое понравившееся. Логин же будет уникальный, по нему будет осуществляться авторизация в приложении. Также, можно будет изменить пароль, прикрепить почту или телефон. Также можно попробовать реализовать двухфакторную аутентификацию через тот же телефон или почту. Фото не обязательно, но можно подумать о выборе одной из нескольких предустановленных иконок. Для информации можно указать дату регистрации.
### Ввод информации

Эта часть приложения должна включить в себя логику по вводу расходов или доходов.
В этом разделе будут расписаны эндпоинты по вводу информации о финансах. На 08.03.2024 подготовлена только модель записи в таблице *spending* в базе данных приложения.  Входные данные можно будет гибко делить на категории и направления траты. Например, категория продукты/еда может иметь такие направления как семья или дом.
### Статистика

В этом разделе нужно расписать эндпоинты о статистике по финансам.
## База данных

Для базы данных будет использоваться PostgreSQL

*Основная таблица - spending*

**id** - уникальный идентификатор, генерируется автоматически для каждой записи.
**Дата** - по умолчанию будет добавляться текущая дата, если нужна другая дата, то у пользователя будет возможность указать другую дату.
**Пользователь** - в этом поле хранится внешний ключ на пользователя, который создал запись.
**Категория траты** - в данное поле необходимо указывать категорию траты. Также это поле является внешнем ключом к таблице с категориями.
**Направление траты**  - Данное поле хранит направление траты. Также это поле является внешним ключом к таблице с направлением траты.
**Дополнительный комментарий** - в это поле можно указать примечание к трате или доходу, если это необходимо, поле может быть nulluble.  
**Кол-во** - собственно само значение денег.

| id  |    Дата    | Пользователь | Категория траты | Направление траты | Дополнительный комментарий | Кол-во |
| --- | :--------: | :----------: | :-------------: | :---------------: | :------------------------: | :----: |
| 1   | 18.12.2023 |      1       |        1        |         2         |           Яблоко           |  100   |
| 2   | 18.12.2023 |      1       |        2        |         1         |       Первая работа        | 60000  |
| 3   | 18.12.2023 |      1       |        3        |         4         |        Зубная нить         |  150   |
| 4   | 18.12.2023 |      1       |        1        |         3         |           Йогурт           |   50   |
| 5   | 18.12.2023 |      1       |        4        |         1         |     Продажа б/у штанов     |  500   |

*Таблица категорий - categories*

**id** - уникальный идентификатор, генерируется автоматически для каждой записи.
**Наименование категории** - категорию можно создавать отдельно от таблицы, она должна быть уникальной и нужно стараться, чтобы категории не повторялись. Например, *еда* и *продукты* это одно и тоже, желательно, чтобы было какое-то одно наименование, чтобы в дальнейшем строился корректный отчет. Также можно будет попробовать реализовать функцию объединения нескольких категорий в одну с указанием одного названия в случае, если всё таки пользователь создал категории одинаковые по смыслу, но разные по наименованию.
**Трата** - это поле указывает на принадлежность записи к трате или доходу.
**Пользователь** - пользователь, который создал категорию

| id  | Наименование категории | Трата | Пользователь |
| :-: | :--------------------- | :---: | :----------: |
|  1  | Продукты               | true  |      1       |
|  2  | Аванс                  | false |      1       |
|  3  | Бытовое                | true  |      1       |
|  4  | Авито                  | false |      1       |

*Таблица направления траты - trend_of_spending*

**id** - уникальный идентификатор, генерируется автоматически для каждой записи.
**Наименование направления** - наименование направления, например, машина, квартира, работа, семья и т.д., что таким образом с помощью комбинации данного поля с полем категорией траты можно добиться наиболее гибкого отчета.
**Пользователь** - пользователь, который создал направление.

| id  | Наименование направления | Пользователь |
| :-: | ------------------------ | ------------ |
|  1  | Доход                    | 1            |
|  2  | Личное                   | 1            |
|  3  | Жена                     | 1            |
|  4  | Семья                    | 1            |

*Таблица пользователей - users*

**id** - уникальный идентификатор, генерируется автоматически для каждой записи.
**Имя пользователя** - в данном поле указывается имя пользователя. Кроме того, в данную таблицу можно добавить различные поля с информацией о пользователе.
**Email** - адрес электронной почты
**Пароль** - в данном столбце будет хранится хэш пароля, созданный с использованием Spring Security
**Роль** - в основном будет использоваться только роль user, но можно подумать про внедрение еще и других ролей, для каких-нибудь других фич

| id  | Имя пользователя | Email           | Пароль     | Роль |
| :-: | ---------------- | --------------- | ---------- | ---- |
|  1  | Дмитрий          | example@mail.ru | Хеш пароля | User |

+ Файл schema.sql:
``` postgresql
DROP TABLE IF EXISTS users, categories, spending, trend_of_spending CASCADE;  
  
CREATE TABLE IF NOT EXISTS users (  
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,  
    name VARCHAR (50) NOT NULL,  
    password VARCHAR NOT NULL,  
    role VARCHAR NOT NULL,  
    email VARCHAR NOT NULL  
);  
  
CREATE TABLE IF NOT EXISTS categories (  
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,  
    name VARCHAR (50) NOT NULL,  
    is_spending BOOLEAN NOT NULL,  
    user_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE,  
    CONSTRAINT uq_name UNIQUE (name)  
);  
  
CREATE TABLE IF NOT EXISTS trend_of_spending (  
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,  
    name VARCHAR (50) NOT NULL,  
    user_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE  
);  
  
CREATE TABLE IF NOT EXISTS spending (  
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,  
    created TIMESTAMP NOT NULL,  
    category_id BIGINT REFERENCES categories (id) ON DELETE CASCADE,  
    trend_id BIGINT REFERENCES trend_of_spending (id) ON DELETE CASCADE,  
    note VARCHAR (200),  
    quantity DOUBLE NOT NULL,  
    user_id BIGINT REFERENCES users (id) ON DELETE CASCADE  
);
```
## Структура проекта

Так как изначально проект строится как веб приложение, то для него нужно предусмотреть сервис gateway, с помощью которого будет производится валидация входных данных и при успешной валидации перенаправлять их на основной сервис (server) где будет происходить вся логика приложения.
### gateway

В сервис gateway будут приходить запросы приложению по REST API. В этом сервисе необходимо настроить валидацию входных данных. Здесь необходимо реализовать контроллеры по пользователям и по данным о тратах и доходах. Реализуем CRUD операции.

+ Пользователи (/users)
  post - создать пользователя, в теле запроса передать json
  get(/{userId}) - получить информацию о пользователе
  pacth(/{userId}) - обновить пользователя, в теле запроса передать json
  delete({userId}) - удалить пользователя

+ Категории (/categories)
  post - создать категорию, в теле передать json
  get - получить все категории
  pacth(/{categoriesId}) - обновить категорию, передать в заголовке
  pacth - объединить две категории категории
  delete(/{categoriesId}) - удалить категорию

+ Траты/доходы(/spending)
  post - создать запись, в заголовке указан пользователь, в теле json объекта spend
  get - получить записи (нужно подумать какие параметры передавать в запросе и какие будут варианты построения отчета)
  pacth(/{spendingId}) - обновление записи в таблице
  delete(/{spendingId}) - дадим пользователю возможность удалять некорректные записи
### server

В сервисе server будет настроена логика и взаимодействие с базой данных. В этом сервисе также будут те же контроллеры, что и в gateway только без валидации, кроме той, которая нуждается в базе данных. Настроить при необходимости DTO, мапперы, поднять базу данных, настроить обработку исключений.
## Аутентификация пользователя

Для регистрации и аутентификации используется Spring Security

+ Регистрация нового пользователя осуществляется по эндпоинту */finserver/auth/sign-up*. В теле запроса необходимо передать json, например:
```
{
    "name": "NeDimon",
    "email": "NeDimon@mail.ru",
    "password": "123asdfzxc"
}
```
после регистрации сервер возвращает токен нового пользователя:
```
{
    "token": "eyJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiUk9MRV9VU0VSIiwiaWQiOjEsImVtYWlsIjoiTmVEaW1vbkBtYWlsLnJ1Iiwic3ViIjoiTmVEaW1vbiIsImlhdCI6MTcwOTkxNTM5NSwiZXhwIjoxNzEwMDU5Mzk1fQ.4tLrZAvIiRjfrgyfroJNJfDDq-l0o6WEod8_gCZjdPE"
}
```
токен нужно включать в заголовок *Authorization* к каждому последующему запросу от этого пользователя.
```
Bearer eyJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiUk9MRV9VU0VSIiwiaWQiOjEsImVtYWlsIjoiTmVEaW1vbkBtYWlsLnJ1Iiwic3ViIjoiTmVEaW1vbiIsImlhdCI6MTcwOTkxNTM5NSwiZXhwIjoxNzEwMDU5Mzk1fQ.4tLrZAvIiRjfrgyfroJNJfDDq-l0o6WEod8_gCZjdPE
```

+ Получить токен зарегистрированного пользователя можно по эндпоинту */finserver/auth/sing-in* передав логин и пароль:
```
{
    "name": "NeDimon",
    "password": "123asdfzxc"
}
```

## Интерфейс пользователя

Нужно продумать интерфейс приложения, с которым пользователь будет взаимодействовать пользователь. Если удастся сделать это самостоятельно. Можно взять за основу пользовательского интерфейса Spring Web.
## Докер

Настроить докер для приложения с помощью docker-compose. Выбрать легковесные дистрибутивы для gateway, server и database. Настроить dockerfile для каждого сервиса.
## Тестирование

Тестирование необходимо выполнять на протяжении всей разработки приложения. Написать JUnit тесты, mock тестирование, интеграционное тестирование, написать автоматические тесты Postman.